import processing.serial.*;
import promidi.*;

MidiIO midiIO;
MidiOut midiOut;

//Midi
String preferredMidiOutput = "MIDI Yoke NT:  5";
int[] controllerValue = new int[6];
int[] controllerValueOld = new int[6];
int analogDamp = 2;

//Constants
int NUM_INPUTS = 6;               //number of analog inputs
int SERIAL_UPDATE_RATE = 10;     //period between updates in milliseconds
int MAX_X = 512;                  //window size x
int MAX_Y = 256;                  //window size y

//Arduino
int ANALOGVAL_MAX = 1024;        //maximum analog value
int[] analogValue = new int[NUM_INPUTS];
int[] analogValueOld = new int[NUM_INPUTS];
int[] analogValueLastTime = new int[NUM_INPUTS];
int[] switchState = new int[NUM_INPUTS];
int[] switchStateOld = new int[NUM_INPUTS];

//Serial
Serial port;

// buffer and value variable for each value and boolean to switch between values
boolean firstContact = false;
int serialCount = 0;
int[] serialInArray = new int[NUM_INPUTS*2];
int[] serialValue = new int[NUM_INPUTS];

//Drawing

int NUM_COLUMNS = 6;
int NUM_ROWS = 4;
int columnWidth = MAX_X / NUM_COLUMNS;
int rowHeight = MAX_Y / NUM_ROWS;

color off = color(200,200,255);
color on = color(84, 145, 158);
PFont fontA;

//General
int i, j, k;

void setup() {

  size(MAX_X, MAX_Y);
  // Set the font and its size (in units of pixels)
  fontA = loadFont("LucidaSans-16.vlw");
  textFont(fontA, 16);
  
  initializeSerial(1, 9600);
  initializeVars();
  initializeMidi();
}

void draw() {

  //PROCESS THE LATEST SERIAL INPUT
  processInput();

  //PROCESS AND OUTPUT MIDI
  processMidi();
  
  //UPDATE THE DISPLAY
  updateDisplay();
  
  //IF NO SERIAL CONTACT HAS BEEN MADE
  if (firstContact == false) {
    delay(300);
    port.write(65);
  } //else {
  //GRAB SERIAL DATA IF SERIAL TIMER HAS TICKED OVER
    //if ((serialCount==0)&&(timer_Serial(SERIAL_UPDATE_RATE))) port.write(65);
 // }
  
  //delay(10);
}

void initializeVars()
{
  for (int i = 0; i < NUM_INPUTS; i++)
  {
    analogValue[i] = 0;
    analogValueOld[i] = -1;
    switchState[i] = 1;
    switchStateOld[i] = -1;
  }
}

//To Get the on/off value from the analog input which is sent to the text file the webpage reads from on the server
void processInput()
{
   for (int i = 0; i < NUM_INPUTS; i++) {
      if (analogValue[i] < 500)
         switchState[i] = 0;
      else
         switchState[i] = 1;
  }
}

void processMidi()
{
  int analogValueDiff;
  for (int i = 0; i < NUM_INPUTS; i++) {
    analogValueDiff = abs(analogValue[i]-analogValueOld[i]);
    
    if (analogValueDiff > analogDamp){
      controllerValue[i] = constrain(analogValue[i] / 8, 0, 127);
      midiOut.sendController(
        new Controller(i+20,controllerValue[i])
      );
    }
    
    controllerValueOld[i] = controllerValue[i];
    analogValueOld[i] = analogValue[i];
  }
}

//Graphical interface update, to show if processing is picking up the analog input in a nice pretty fashion.
void updateDisplay()
{
  background(off);
  stroke(on);
  text("val" , 2, rowHeight * 2 + (rowHeight/2));
  
  
  for (int i = 0; i < NUM_INPUTS; i++) 
  {
    stroke(255);
    fill(255);
   //Draw circles representing analog value
    ellipse(i*columnWidth+columnWidth/2, rowHeight/2,
      analogValue[i] / (ANALOGVAL_MAX / rowHeight),analogValue[i] / (ANALOGVAL_MAX / rowHeight));
  
   //Draw dots representing ON/OFF state
    if (switchState[i] == 1)
      ellipse(i*columnWidth+columnWidth/2, rowHeight+rowHeight/2, rowHeight/4, rowHeight/4);
 
   //Print values data
    stroke(0,0,0);
    fill(0,0,0);
    text(analogValue[i]/8, i * columnWidth + columnWidth/3, rowHeight * 2 + (rowHeight/2));
    text(switchState[i], i * columnWidth + columnWidth/3, rowHeight * 3 + (rowHeight/2));
  }
}


int timerPrevSerial = 0;
boolean timer_Serial(int timerPeriod)
{
  boolean ret = false;
  int timer = millis();
  if ((timer - timerPrevSerial) > timerPeriod)
  {
    ret = true;
    timerPrevSerial = timer;
  }
  return ret;
}

void initializeMidi()
{
   //get an instance of MidiIO
  midiIO = MidiIO.getInstance(this);
  
  //print a list with all available devices
  midiIO.printDevices();
  
  midiOut = midiIO.getMidiOut(1, preferredMidiOutput);
}

void initializeSerial(int portNum, int baudrate)
{
  // establish serial port connection      
  // port = new Serial(this, portname, baudrate);
  println("Available serial ports:");
  println(Serial.list());

  // Uses the first available port
  port = new Serial(this, Serial.list()[portNum], baudrate);
  println(port);
  port.write(65);
}

void serialEvent(Serial port) {
  // if this is the first byte received, 
  // take note of that fact:
  if (firstContact == false) {
    firstContact = true; 
  }
  // Add the latest byte from the serial port to array:
  serialInArray[serialCount] = port.read();
  serialCount++;

  // If we have 12 bytes:
  if (serialCount > 11 ) {
    for (i=0;i<6;i++) {
      serialValue[i] = serialInArray[i*2];
      serialValue[i] <<= 8;
      serialValue[i] = serialValue[i] + serialInArray[i*2 + 1];
      
      int time = millis();
      if (abs(analogValue[i] - serialValue[i]) > 0)
      {
        if ((time - analogValueLastTime[i]) > 70)
        {
          analogValue[i] = serialValue[i];
          analogValueLastTime[i] = time;
        }
      }
    }
    // Reset serialCount:
    serialCount = 0;
    port.write(65);
  }
}


